package sf.database.jdbc.type;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sf.database.annotations.Type;
import sf.database.meta.ColumnMapping;
import sf.database.util.JdbcUtils;

import javax.persistence.AttributeConverter;
import javax.persistence.Convert;
import javax.persistence.EnumType;
import javax.persistence.TemporalType;
import java.lang.reflect.Method;
import java.sql.Types;
import java.util.Date;

/**
 * 提供一些与 JDBC 有关的帮助函数
 * @author
 */
public abstract class Jdbcs {
    private static final Logger log = LoggerFactory.getLogger(Jdbcs.class);
    public static TypeHandlerRegistry registry = new TypeHandlerRegistry();

    /**
     * 空值适配器
     */
    @SuppressWarnings("rawtypes")
    public static final TypeHandler asNull = new NullType();

    /**
     * 注册一个自定义JdbcValue,若adaptor为null,则取消注册
     * @param clazz   类名
     * @param adaptor 值适配器实例,若为null,则取消注册
     * @return 原有的值适配器
     */
    public static TypeHandler<?> registerCustomType(Class clazz, TypeHandler<?> adaptor) {
        if (adaptor == null) {
            return registry.removeCustomTypeHandler(clazz);
        }
        return registry.registerCustom(clazz, adaptor.getSqlType(), adaptor);
    }

    public static TypeHandler<?> getDB2BeanMappingType(Class<?> mirror) {
        return getDB2BeanMappingType(mirror, 0);
    }

    public static TypeHandler<?> getDB2BeanMappingType(Class<?> mirror, int sqlType) {
        TypeHandler<?> custom = registry.getCustomTypeHandler(mirror);
        if (custom != null) {
            return custom;
        } else if (mirror == String.class || mirror == CharSequence.class) {
            if (sqlType == Types.ROWID) {
                return registry.getTypeHandler(String.class, Types.ROWID);
            }
        } else if (mirror.isEnum()) {
            // Enum
            if (JdbcUtils.isNumeric(sqlType)) {
                return new EnumOrdinalTypeHandler(mirror);
            }
            return new EnumStringTypeHandler(mirror);
        }
        // 默认情况
        return registry.getTypeHandler(mirror, sqlType);
    }

    @SuppressWarnings("unchecked")
    public static <T> TypeHandler<T> getBean2DBMappingType(ColumnMapping column) {
        Class<?> mirror = column.getClz();
        TypeHandler<?> custom = registry.getCustomTypeHandler(mirror);
        if (custom != null) {
            return (TypeHandler<T>) custom;
        }
        Type type = column.getType();
        Convert convert = column.getConvert();
        if (type != null || convert != null) {
            //自定义转换类型必须提供空参构造.
            TypeHandler<?> typeHandler = null;
            if (type != null) {
                try {
                    Class<? extends TypeHandler<?>> typeClass = (Class<? extends TypeHandler<?>>) type.value();
                    if (typeClass == NullType.class) {
                        //找不到,抛出异常
                        typeHandler = registry.getTypeHandlerNullThrowException(type.javaType(), type.sqlType());
                    } else {
                        typeHandler = registry.getMappingTypeHandler(typeClass);
                        if (typeHandler == null) {
                            typeHandler = typeClass.newInstance();
                            Method[] declaredMethods = typeClass.getDeclaredMethods();
                            for (int i = 0; i < declaredMethods.length; i++) {
                                Method m = declaredMethods[i];
                                //需要设置自定义返回类型
                                if (m.getName().contains("setDefaultJavaType")) {
                                    typeHandler.setDefaultJavaType((Class) mirror);
                                    break;
                                }
                            }
                            typeHandler.setColumnMapping(column);
                        }
                    }
                } catch (InstantiationException | IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
            if (convert != null && convert.converter() != void.class) {
                try {
                    AttributeConverterType<T, Object> attributeConverterType = new AttributeConverterType<>();
                    AttributeConverter<T, Object> attributeConverter = (AttributeConverter<T, Object>) convert.converter().newInstance();
                    if (typeHandler == null) {
                        throw new RuntimeException("请指定@Type类型");
                    }
                    attributeConverterType.setTypeHandler((TypeHandler<Object>) typeHandler);
                    attributeConverterType.setAttributeConverter(attributeConverter);
                    typeHandler = attributeConverterType;
                } catch (IllegalAccessException | InstantiationException e) {
                    throw new RuntimeException(e);
                }
            }
            return (TypeHandler<T>) typeHandler;

        } else if (mirror == String.class || mirror == CharSequence.class) {
            // String char
            if (column.isLob()) {
                return (TypeHandler<T>) registry.getTypeHandler(String.class, Types.CLOB);
            }
        } else if (mirror.isEnum()) {
            // Enum
            if (column.getEnumerated() != null && column.getEnumerated().value() == EnumType.ORDINAL) {
                return new EnumOrdinalTypeHandler(mirror);
            }
            return new EnumStringTypeHandler(mirror);
        } else if (Date.class.isAssignableFrom(mirror)) {
            if (column.getTemporal() != null) {
                if (column.getTemporal().value() == TemporalType.TIME) {
                    return (TypeHandler<T>) registry.getTypeHandler(Date.class, Types.TIME);
                } else if (column.getTemporal().value() == TemporalType.TIMESTAMP) {
                    return (TypeHandler<T>) registry.getTypeHandler(Date.class, Types.TIMESTAMP);
                } else if (column.getTemporal().value() == TemporalType.DATE) {
                    return (TypeHandler<T>) registry.getTypeHandler(Date.class, Types.DATE);
                }
            }
            //默认为时间戳
            return (TypeHandler<T>) registry.getTypeHandler(Date.class, Types.TIMESTAMP);
        }

        // 默认情况
        TypeHandler<T> typeHandler = (TypeHandler<T>) registry.getTypeHandler(mirror);
        if (typeHandler == null) {
            typeHandler = (TypeHandler<T>) registry.getTypeHandler(Object.class, Types.JAVA_OBJECT);
        }
        return typeHandler;
    }

    /**
     * 设置 jdbc sql type
     * @param column
     */
    public static void setJdbcSqlType(ColumnMapping column) {
        TypeHandler<?> type = column.getHandler();
        if (type == null) {
            type = getBean2DBMappingType(column);
            column.setHandler((TypeHandler<Object>) type);
        }
        if (type != null) {
            column.setSqlType(type.getSqlType());
        } else {
            column.setSqlType(Types.OTHER);
        }
        if (column.getColumn() != null && (column.getColumn().scale() > 0 || column.getColumn().precision() > 0)) {
            Class<?> clz = column.getClz();
            if (clz == Double.class || clz == Double.TYPE || clz == Float.class || clz == Float.TYPE) {
                column.setSqlType(Types.DECIMAL);
            } else if (clz == Integer.class || clz == Integer.TYPE || clz == Long.class || clz == Long.TYPE) {
                column.setSqlType(Types.NUMERIC);
            }
        }
    }
}
